#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
XDMA驱动 - PyQt6版本
对应原C++项目的XDMA类，用于与FPGA硬件通信
"""

import os
import sys
import time
import struct
import threading
from ctypes import (
    Structure, Union, POINTER, byref, cast, sizeof,
    c_char, c_ushort, c_ulong, c_void_p,
    windll, WinDLL, get_last_error, FormatError
)
from ctypes.wintypes import DWORD, HANDLE, BOOL, LPCSTR, LPSTR, ULONG

# Windows SetupAPI constants and structures
GUID_DEVINTERFACE_XDMA = "{D5E6F5E4-6B4F-4EAD-8B2B-4B40B7C5E5A8}"

DIGCF_PRESENT = 0x00000002
DIGCF_DEVICEINTERFACE = 0x00000010

SP_DEVICE_INTERFACE_DATA = type('SP_DEVICE_INTERFACE_DATA', (Structure,), {
    '_fields_': [
        ('cbSize', DWORD),
        ('InterfaceClassGuid', c_char * 16),
        ('Flags', DWORD),
        ('Reserved', c_ulong),
    ]
})

PSP_DEVICE_INTERFACE_DATA = POINTER(SP_DEVICE_INTERFACE_DATA)

SP_DEVICE_INTERFACE_DETAIL_DATA_W = type('SP_DEVICE_INTERFACE_DETAIL_DATA_W', (Structure,), {
    '_fields_': [
        ('cbSize', DWORD),
        ('DevicePath', c_ushort * 1),  # Variable length
    ]
})

PSP_DEVICE_INTERFACE_DETAIL_DATA_W = POINTER(SP_DEVICE_INTERFACE_DETAIL_DATA_W)

# Load Windows DLLs
try:
    setupapi = WinDLL('setupapi.dll')
    kernel32 = WinDLL('kernel32.dll')
except Exception as e:
    print(f"无法加载Windows DLL: {e}")
    sys.exit(1)

# Setup function signatures
SetupDiGetClassDevsW = setupapi.SetupDiGetClassDevsW
SetupDiGetClassDevsW.argtypes = [c_void_p, c_void_p, c_void_p, DWORD]
SetupDiGetClassDevsW.restype = HANDLE

SetupDiEnumDeviceInterfaces = setupapi.SetupDiEnumDeviceInterfaces
SetupDiEnumDeviceInterfaces.argtypes = [HANDLE, c_void_p, c_void_p, DWORD, PSP_DEVICE_INTERFACE_DATA]
SetupDiEnumDeviceInterfaces.restype = BOOL

SetupDiGetDeviceInterfaceDetailW = setupapi.SetupDiGetDeviceInterfaceDetailW
SetupDiGetDeviceInterfaceDetailW.argtypes = [HANDLE, PSP_DEVICE_INTERFACE_DATA, PSP_DEVICE_INTERFACE_DETAIL_DATA_W, DWORD, POINTER(DWORD), c_void_p]
SetupDiGetDeviceInterfaceDetailW.restype = BOOL

SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList
SetupDiDestroyDeviceInfoList.argtypes = [HANDLE]
SetupDiDestroyDeviceInfoList.restype = BOOL

# Kernel32 function signatures
CreateFileW = kernel32.CreateFileW
CreateFileW.argtypes = [c_void_p, DWORD, DWORD, c_void_p, DWORD, DWORD, HANDLE]
CreateFileW.restype = HANDLE

ReadFile = kernel32.ReadFile
ReadFile.argtypes = [HANDLE, c_void_p, DWORD, POINTER(DWORD), c_void_p]
ReadFile.restype = BOOL

WriteFile = kernel32.WriteFile
WriteFile.argtypes = [HANDLE, c_void_p, DWORD, POINTER(DWORD), c_void_p]
WriteFile.restype = BOOL

CloseHandle = kernel32.CloseHandle
CloseHandle.argtypes = [HANDLE]
CloseHandle.restype = BOOL


class XDMADriver:
    """XDMA驱动类"""

    def __init__(self, buffer_size=1024*1024):
        self.buffer_size = buffer_size
        self.devices_opened = False

        # Device handles
        self.c2h0_device = None
        self.h2c0_device = None
        self.user_device = None
        self.event0_device = None
        self.event1_device = None
        self.event2_device = None

        # 打开设备
        self.open_device()

    def open_device(self):
        """打开XDMA设备"""
        try:
            device_path = self._find_xdma_device()
            if not device_path:
                raise Exception("未找到XDMA设备")

            # 构造设备路径
            base_path = device_path.encode('utf-16le')

            # 打开c2h0设备 (从设备到主机)
            c2h0_path = base_path + b'\\c2h_0\x00'
            self.c2h0_device = CreateFileW(
                c2h0_path,  # lpFileName
                0x80000000,  # GENERIC_READ
                0,           # dwShareMode
                None,        # lpSecurityAttributes
                3,           # OPEN_EXISTING
                0,           # dwFlagsAndAttributes
                None         # hTemplateFile
            )

            if self.c2h0_device == HANDLE(-1).value:
                error_code = get_last_error()
                raise Exception(f"无法打开c2h0设备: {FormatError(error_code)}")

            # 打开h2c0设备 (从主机到设备)
            h2c0_path = base_path + b'\\h2c_0\x00'
            self.h2c0_device = CreateFileW(
                h2c0_path,
                0x40000000,  # GENERIC_WRITE
                0,
                None,
                3,
                0,
                None
            )

            if self.h2c0_device == HANDLE(-1).value:
                error_code = get_last_error()
                raise Exception(f"无法打开h2c0设备: {FormatError(error_code)}")

            # 打开user设备
            user_path = base_path + b'\\user\x00'
            self.user_device = CreateFileW(
                user_path,
                0x80000000 | 0x40000000,  # GENERIC_READ | GENERIC_WRITE
                0,
                None,
                3,
                0,
                None
            )

            if self.user_device == HANDLE(-1).value:
                error_code = get_last_error()
                raise Exception(f"无法打开user设备: {FormatError(error_code)}")

            # 打开事件设备
            self._open_event_devices(base_path)

            self.devices_opened = True
            print("XDMA设备打开成功")

        except Exception as e:
            print(f"打开XDMA设备失败: {e}")
            self.close()
            raise

    def _find_xdma_device(self):
        """查找XDMA设备路径"""
        # 创建GUID结构
        guid = (c_char * 16)(*struct.pack('<IHHBBBBBBBB', 0xD5E6F5E4, 0x6B4F, 0x4EAD, 0x8B, 0x2B, 0x4B, 0x40, 0xB7, 0xC5, 0xE5, 0xA8))

        # 获取设备信息集
        device_info = SetupDiGetClassDevsW(
            byref(guid),  # ClassGuid
            None,         # Enumerator
            None,         # hwndParent
            DIGCF_PRESENT | DIGCF_DEVICEINTERFACE  # Flags
        )

        if device_info == HANDLE(-1).value:
            error_code = get_last_error()
            raise Exception(f"SetupDiGetClassDevsW失败: {FormatError(error_code)}")

        try:
            # 枚举设备接口
            device_interface = SP_DEVICE_INTERFACE_DATA()
            device_interface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA)

            if not SetupDiEnumDeviceInterfaces(
                device_info,
                None,
                byref(guid),
                0,  # MemberIndex
                byref(device_interface)
            ):
                error_code = get_last_error()
                if error_code != 0x103:  # ERROR_NO_MORE_ITEMS
                    raise Exception(f"SetupDiEnumDeviceInterfaces失败: {FormatError(error_code)}")
                return None

            # 获取设备路径长度
            required_size = DWORD()
            if not SetupDiGetDeviceInterfaceDetailW(
                device_info,
                byref(device_interface),
                None,  # DeviceInterfaceDetailData
                0,     # DeviceInterfaceDetailDataSize
                byref(required_size),
                None   # DeviceInfoData
            ):
                error_code = get_last_error()
                if error_code != 0x7A:  # ERROR_INSUFFICIENT_BUFFER
                    raise Exception(f"SetupDiGetDeviceInterfaceDetailW失败: {FormatError(error_code)}")

            # 分配缓冲区
            detail_data = (c_ushort * (required_size.value // 2))()
            detail_struct = SP_DEVICE_INTERFACE_DETAIL_DATA_W()
            detail_struct.cbSize = 8  # sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) on 64-bit
            detail_struct.DevicePath = detail_data

            # 获取设备路径
            if not SetupDiGetDeviceInterfaceDetailW(
                device_info,
                byref(device_interface),
                byref(detail_struct),
                required_size,
                None,
                None
            ):
                error_code = get_last_error()
                raise Exception(f"SetupDiGetDeviceInterfaceDetailW失败: {FormatError(error_code)}")

            # 转换路径
            path_length = 0
            while detail_struct.DevicePath[path_length] != 0:
                path_length += 1

            device_path = bytes(detail_struct.DevicePath[:path_length]).decode('utf-16le')
            return device_path

        finally:
            SetupDiDestroyDeviceInfoList(device_info)

    def _open_event_devices(self, base_path):
        """打开事件设备"""
        # 事件设备名称列表
        event_names = [b'\\event_0\x00', b'\\event_1\x00', b'\\event_2\x00']

        # 动态创建事件设备句柄
        for i, event_name in enumerate(event_names):
            event_path = base_path + event_name
            event_device = CreateFileW(
                event_path,
                0x80000000,  # GENERIC_READ
                0,
                None,
                3,  # OPEN_EXISTING
                0,
                None
            )

            if event_device == HANDLE(-1).value:
                error_code = get_last_error()
                print(f"警告: 无法打开{event_name.decode()}: {FormatError(error_code)}")
                continue

            # 设置事件设备句柄
            if i == 0:
                self.event0_device = event_device
            elif i == 1:
                self.event1_device = event_device
            elif i == 2:
                self.event2_device = event_device

    def read_data(self, address, size):
        """读取数据"""
        if not self.devices_opened:
            raise Exception("设备未打开")

        # 这里需要实现DMA读取逻辑
        # 暂时返回模拟数据
        return bytes(size)

    def write_data(self, address, data):
        """写入数据"""
        if not self.devices_opened:
            raise Exception("设备未打开")

        # 这里需要实现DMA写入逻辑
        pass

    def write_user_register(self, offset, value):
        """写入用户寄存器"""
        if not self.user_device:
            raise Exception("用户设备未打开")

        # 构造数据
        data = struct.pack('<I', value)

        # 写入寄存器
        bytes_written = DWORD()
        if not WriteFile(
            self.user_device,
            data,
            len(data),
            byref(bytes_written),
            None
        ):
            error_code = get_last_error()
            raise Exception(f"写入寄存器失败: {FormatError(error_code)}")

    def start_camera(self):
        """启动摄像头"""
        # 这里应该发送启动摄像头的指令
        # 暂时只打印信息
        print("启动摄像头")

    def stop_camera(self):
        """停止摄像头"""
        # 这里应该发送停止摄像头的指令
        # 暂时只打印信息
        print("停止摄像头")

    def irq0(self):
        """中断0处理 - 对应C++版本的irq0()"""
        # 这里应该处理中断0的逻辑
        # 暂时只打印信息
        pass

    def irq1(self):
        """中断1处理 - 对应C++版本的 irq1()"""
        # 这里应该处理中断1的逻辑
        # 暂时只打印信息
        pass

    def irq2(self):
        """中断2处理 - 对应C++版本的 irq2()"""
        # 这里应该处理中断2的逻辑
        # 暂时只打印信息
        pass

    def close(self):
        """关闭设备"""
        handles = [
            self.c2h0_device, self.h2c0_device, self.user_device,
            self.event0_device, self.event1_device, self.event2_device
        ]

        for handle in handles:
            if handle and handle != HANDLE(-1).value:
                CloseHandle(handle)

        self.devices_opened = False
        print("XDMA设备已关闭")




# 测试函数
def test_xdma_driver():
    """测试XDMA驱动"""
    try:
        driver = XDMADriver()
        print("XDMA驱动测试成功")

        # 测试寄存器写入
        driver.write_user_register(0x18, 0x30000000)
        driver.write_user_register(0x1c, 0x31000000)
        print("寄存器写入测试成功")

        driver.close()
        print("XDMA驱动测试完成")

    except Exception as e:
        print(f"XDMA驱动测试失败: {e}")


if __name__ == "__main__":
    test_xdma_driver()
